package com.ews.onlineexchange.service;

import java.io.Serializable;
import java.util.List;

import javax.inject.Inject;
import javax.persistence.EntityManager;

import org.apache.lucene.search.Query;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.jpa.Search;

import com.ews.onlineexchange.model.DataEntity;

public abstract class FullTextEntityQuery<T extends DataEntity> implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public static final int MAX_RESULTS_FEW = 10;
	public static final int MAX_RESULTS_NORMAL = 15;
	public static final int MAX_RESULTS_LOT = 20;
	
	Integer firstResult = 0;
	Integer maxResults = MAX_RESULTS_NORMAL;
	String order;
	Long resultCount;
	
	List<T> resultList;	
	
	@Inject
	EntityManager entityManager;

	public FullTextEntityManager getFullTextEntityManager() {
		return Search.getFullTextEntityManager(entityManager);
	}
	
    public List<T> getResultList() {
    	if (resultList == null)
    		init();
    	return truncResultList(resultList);
    }
    
    private void init() {
    	initResultList();
		initResultCount();
    }
    
    private void initResultList() {    	
    	FullTextQuery query = createQuery();
    	query.setMaxResults(getMaxResults());
    	query.setFirstResult(getFirstResult());
    	setResultList(query.getResultList());   
    	     		 
    }
    
    private void initResultCount() {	
    	FullTextQuery query = createQuery();
    	setResultCount((long) query.getResultSize());
    	  
    }
    
    abstract protected Query createLuceneQuery();
	
    abstract protected org.hibernate.search.jpa.FullTextQuery createQuery();

	public void refresh() {
		resultCount = null;
	    resultList = null;
	}
		
	public boolean isNextExists() {
		getResultList();
		return resultList!=null && getMaxResults()!=null &&
	             resultList.size() > getMaxResults();
	}
	
	public boolean isPreviousExists() {
		return getFirstResult() != null && getFirstResult() != 0;
	}
	
	public boolean isPaginated() {
		return isNextExists() || isPreviousExists();
	}
	
	public Long getLastFirstResult() {
		Integer pc = getPageCount();
		return pc == null ? null : (pc.longValue() - 1) * getMaxResults();
	}
	
	/**
	 * Get the index of the first result of the next page
	 * 
	 */
	public int getNextFirstResult() {
		Integer fr = getFirstResult();
		return (fr == null ? 0 : fr) + getMaxResults();
	}
	
	/**
	 * Get the index of the first result of the previous page
	 * 
	 */
	public int getPreviousFirstResult() {
		Integer fr = getFirstResult();
		Integer mr = getMaxResults();
		return mr >= (fr == null ? 0 : fr) ? 0 : fr - mr;
	}
	
	/**
	 * Get the total number of pages
	 * 
	 */
	public Integer getPageCount() {
		if (getMaxResults() == null) {
			return null;
		} else {
			int rc = getResultCount().intValue();
			int mr = getMaxResults().intValue();
			int pages = rc / mr;
			return rc % mr == 0 ? pages : pages + 1;
		}
	}
	
	protected List<T> truncResultList(List<T> results) {
		Integer mr = getMaxResults();
		if (mr != null && results.size() > mr) {
			return results.subList(0, mr);
		} else {
			return results;
		}
	}
	
	public Integer getFirstResult() {
		return firstResult;
	}
	
	public void setFirstResult(Integer firstResult) {
		this.firstResult = firstResult;
	}
	
	public Integer getMaxResults() {
		return maxResults;
	}
	
	public void setMaxResults(Integer maxResults) {
		this.maxResults = maxResults;
	}

	public void setResultList(List<T> resultList) {
		this.resultList = resultList;
	}

	public Long getResultCount() {
		if (resultCount == null)
			init();
		return resultCount;
	}

	public void setResultCount(Long resultCount) {
		this.resultCount = resultCount;
	}

}
